#include "tcpci.h"
#include "tcpci_core.h"
#include "tcpm.h"
#include "sgmdev_pd.h"

#undef LOG_TAG
#define LOG_TAG    "tcpci"
#include "elog.h"

#define TCPC_NOTIFY_OVERTIME	(20) /* ms */
int tcpci_notify_hard_reset_state(struct sgm41620_tcpc *tcpc, uint8_t state)
{
	if (state >= TCP_HRESET_SIGNAL_SEND)
		tcpc->pd_wait_hard_reset_complete = true;
	else if (tcpc->pd_wait_hard_reset_complete)
		tcpc->pd_wait_hard_reset_complete = false;
	else
		return 0;
	
	/*struct tcp_notify tcp_noti;

	tcp_noti.hreset_state.state = state;

	if (state >= TCP_HRESET_SIGNAL_SEND)
		tcpc->pd_wait_hard_reset_complete = true;
	else if (tcpc->pd_wait_hard_reset_complete)
		tcpc->pd_wait_hard_reset_complete = false;
	else
		return 0;

	return tcpc_check_notify_time(tcpc, &tcp_noti,
		TCP_NOTIFY_IDX_MISC, TCP_NOTIFY_HARD_RESET_STATE);*/
	return 0;
}

bool tcpci_check_vsafe0v(struct sgm41620_tcpc *tcpc)
{
	//log_i("vbus_level =%d\r\n",tcpc->vbus_level);
	return tcpc->vbus_level == TCPC_VBUS_SAFE0V;
}

int tcpci_alert_status_clear(struct sgm41620_tcpc *tcpc, uint32_t mask)
{
    if (p_tcpc_dev->ops->alert_status_clear)
        return p_tcpc_dev->ops->alert_status_clear(mask);
    else
        return -1;
}

int tcpci_fault_status_clear(struct sgm41620_tcpc *tcpc, uint8_t status)
{
    if (p_tcpc_dev->ops->fault_status_clear)
        return p_tcpc_dev->ops->fault_status_clear(status);
    else
        return -1;
}


int tcpci_set_alert_mask(struct sgm41620_tcpc *tcpc, uint32_t mask)
{
    if (p_tcpc_dev->ops->set_alert_mask)
        return p_tcpc_dev->ops->set_alert_mask(mask);
    else
        return -1;
}

int tcpci_get_alert_mask(struct sgm41620_tcpc *tcpc, uint32_t *mask)
{
    if (p_tcpc_dev->ops->get_alert_mask)
        return p_tcpc_dev->ops->get_alert_mask(mask);
    else
        return -1;
}

int tcpci_get_alert_status(struct sgm41620_tcpc *tcpc, uint32_t *alert)
{
    if (p_tcpc_dev->ops->get_alert_status)
        return p_tcpc_dev->ops->get_alert_status(alert);
    else
        return -1;
}

int tcpci_get_fault_status(struct sgm41620_tcpc *tcpc, uint8_t *fault)
{
    if (p_tcpc_dev->ops->get_fault_status)
        return p_tcpc_dev->ops->get_fault_status(fault);
    else
        return -1;
}

int tcpci_init_alert_mask(struct sgm41620_tcpc *tcpc)
{
    if (p_tcpc_dev->ops->init_alert_mask)
        return p_tcpc_dev->ops->init_alert_mask();
    else
        return -1;
}

int tcpci_set_polarity(struct sgm41620_tcpc *tcpc, int polarity)
{
    if (p_tcpc_dev->ops->set_polarity)
        return p_tcpc_dev->ops->set_polarity(polarity);
    else
        return -1;
}

int tcpci_set_vconn(struct sgm41620_tcpc *tcpc, bool enable)
{
    if (p_tcpc_dev->ops->set_vconn == NULL)
        return -1;

    if (tcpc->tcpc_source_vconn == enable)
        return 0;

    tcpc->tcpc_source_vconn = enable;

    return p_tcpc_dev->ops->set_vconn(enable);
}

int tcpci_source_vbus(
	struct sgm41620_tcpc *tcpc, uint8_t type, int mv, int ma)
{

#if 1
	if (type >= TCP_VBUS_CTRL_PD &&
			p_pd_port->pe_data.pd_prev_connected)
		type |= TCP_VBUS_CTRL_PD_DETECT;
#endif	/* CONFIG_USB_POWER_DELIVERY */

	if (ma < 0) {
		if (mv != 0) {
			switch (tcpc->typec_local_rp_level) {
			case TYPEC_RP_3_0:
				ma = 3000;
				break;
			case TYPEC_RP_1_5:
				ma = 1500;
				break;
			case TYPEC_RP_DFT:
			default:
				ma = CONFIG_TYPEC_SRC_CURR_DFT;
				break;
			}
		} else
			ma = 0;
	}

	//log_i("source_vbus: %d mV, %d mA\n", mv, ma);
	//sgm41620_disable_hiz();
    if (tcpc->ops->set_vbus == NULL)
        return -1;

	if(mv)
		tcpc->ops->set_vbus(true);
	else
        tcpc->ops->set_vbus(false);

	return 0;
	/*return tcpc_check_notify_time(tcpc, &tcp_noti,
		TCP_NOTIFY_IDX_VBUS, TCP_NOTIFY_SOURCE_VBUS);*/
}

int tcpci_sink_vbus(struct sgm41620_tcpc *tcpc, uint8_t type, int mv, int ma)
{
#if CONFIG_USB_POWER_DELIVERY
    if (type >= TCP_VBUS_CTRL_PD && p_pd_port->pe_data.pd_prev_connected)
        type |= TCP_VBUS_CTRL_PD_DETECT;
#endif

    if (ma < 0) {
        if (mv != 0) {
            switch (tcpc->typec_remote_rp_level) {
            case TYPEC_CC_VOLT_SNK_1_5:
                ma = 1500;
                break;
            case TYPEC_CC_VOLT_SNK_3_0:
                ma = 3000;
                break;
            case TYPEC_CC_VOLT_SNK_DFT:
            default:
                ma = tcpc->typec_usb_sink_curr;
                break;
            }
#if CONFIG_TYPEC_SNK_CURR_LIMIT > 0
            if (ma > CONFIG_TYPEC_SNK_CURR_LIMIT)
                ma = CONFIG_TYPEC_SNK_CURR_LIMIT;
#endif
        } else {
            ma = 0;
        }
    }

    // TODO: set sink load capability

    return 0;
}


int tcpci_dp_status_update(struct sgm41620_tcpc *tcpc, uint32_t dp_status)
{
	log_i("Status0: 0x%x\n", dp_status);
	//tcpci_report_hpd_state(tcpc, dp_status);
	return 0;
}


int tcpci_disable_vbus_control(struct sgm41620_tcpc *tcpc)
{
#if CONFIG_TYPEC_USE_DIS_VBUS_CTRL
	struct tcp_notify tcp_noti;

	log_i("disable_vbus\n");

	return tcpc_check_notify_time(tcpc, &tcp_noti,
		TCP_NOTIFY_IDX_VBUS, TCP_NOTIFY_DIS_VBUS_CTRL);
#else
	tcpci_sink_vbus(tcpc, TCP_VBUS_CTRL_REMOVE, TCPC_VBUS_SINK_0V, 0);
	tcpci_source_vbus(tcpc, TCP_VBUS_CTRL_REMOVE, TCPC_VBUS_SOURCE_0V, 0);
	return 0;
#endif	/* CONFIG_TYPEC_USE_DIS_VBUS_CTRL */
}

static int __tcpci_enable_force_discharge(
	struct sgm41620_tcpc *tcpc, bool en, int mv)
{
	int ret = 0;

#if CONFIG_TYPEC_CAP_FORCE_DISCHARGE
#if CONFIG_TCPC_FORCE_DISCHARGE_IC
	if (tcpc->typec_force_discharge != en) {
		tcpc->typec_force_discharge = en;
        if (tcpc->ops->set_force_discharge)
            ret = tcpc->ops->set_force_discharge(en, mv);
	}
#endif	/* CONFIG_TCPC_FORCE_DISCHARGE_IC */
#endif	/* CONFIG_TYPEC_CAP_FORCE_DISCHARGE */

	return ret;
}

int tcpci_enable_force_discharge(struct sgm41620_tcpc *tcpc, bool en, int mv)
{
	int ret = 0;

#if CONFIG_TYPEC_CAP_FORCE_DISCHARGE
	ret = __tcpci_enable_force_discharge(tcpc, en, mv);
#if CONFIG_TCPC_FORCE_DISCHARGE_EXT
	//ret = __tcpci_enable_ext_discharge(tcpc, en);
#endif	/* CONFIG_TCPC_FORCE_DISCHARGE_EXT */
#endif	/* CONFIG_TYPEC_CAP_FORCE_DISCHARGE */

	return ret;
}

int tcpci_set_low_power_mode(struct sgm41620_tcpc *tcpc, bool en)
{
    int ret = 0, pull = TYPEC_CC_OPEN;
#if CHIP_TYPE != CHIP_SG41620  // 41620 do not support low power mode
    int i = 0;

    tcpc->typec_lpm = en;
    if (!tcpc_typec_is_cc_open_state(tcpc)) {
        switch (tcpc->typec_role) {
        case TYPEC_ROLE_SNK:
            pull = TYPEC_CC_RD;
            break;
        case TYPEC_ROLE_SRC:
            pull = TYPEC_CC_RP;
            break;
        default:
            pull = TYPEC_CC_DRP;
            break;
        }
    }

    /* [Workaround]
     * rx_buffer can't clear, try to reset protocol before disable bmc clock
     */
    if (en) {
        ret = tcpci_protocol_reset(tcpc);
        for (i = 0; i < 2; i++) {
            ret = tcpci_alert_status_clear(tcpc, TCPC_REG_ALERT_RX_ALL_MASK);
            if (ret < 0)
                log_i("%s:%d clear rx event fail\n", __func__, i);
        }
    }
#endif

    if (tcpc->ops->set_low_power_mode)
        ret = p_tcpc_dev->ops->set_low_power_mode(en, pull);

    return ret;
}

#if SG41620_C_PATCH
extern void tcpc_enable_bist_timer(struct sgm41620_tcpc *tcpc, uint32_t timer_id);
extern void tcpc_disable_bist_timer(struct sgm41620_tcpc *tcpc, uint32_t timer_id);
#endif
int tcpci_set_bist_test_mode(struct sgm41620_tcpc *tcpc, bool en)
{
#if SG41620_C_PATCH
	if(en)
		tcpc_enable_bist_timer(p_tcpc_dev,0);
	else
        tcpc_disable_bist_timer(p_tcpc_dev,0);
#endif

    if (tcpc->ops->set_bist_test_mode)
        tcpc->ops->set_bist_test_mode(en);

	return 0;
}

int tcpci_transmit(struct sgm41620_tcpc *tcpc,
	enum tcpm_transmit_type type, uint16_t header, const uint32_t *data)
{
    if (tcpc->ops->transmit)
        return tcpc->ops->transmit(type, header, data);

    return 0;
}

#if CONFIG_USB_PD_RETRY_CRC_DISCARD
int tcpci_retransmit(struct sgm41620_tcpc *tcpc)
{
    if (tcpc->ops->retransmit)
        return tcpc->ops->retransmit();
    else
        return -1;
}
#endif	/* CONFIG_USB_PD_RETRY_CRC_DISCARD */
